package org.wheel.plugins.toolkit.sql.helper;

import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.wheel.plugins.toolkit.sql.core.AbstractSegment;
import org.wheel.plugins.toolkit.sql.core.AbstractWrapper;
import org.wheel.plugins.toolkit.sql.core.SqlSegment;

/**
 * 字段引用：自动带表别名
 * 支持：字符串字段 / Lambda 字段 / 指定 joinIndex
 */
@Getter
public final class ColumnRef extends AbstractSegment {

    private String tableAlias;
    @Getter
    private final String column;   // 数据库列名
    private String columnAlias;
    private boolean useColumnAlias;

    private ColumnRef(String tableAlias, String column, String columnAlias,  AbstractWrapper<?, ?> wrapper) {
        this.tableAlias = tableAlias;
        this.column = column;
        this.columnAlias = columnAlias;
        this.useColumnAlias = true;
        this.currentWrapper = wrapper;
    }

    /* ========== 工厂：字符串字段 ========== */
    public static ColumnRef of(String tableAlias, String rawColumn, String columnAlias, AbstractWrapper<?, ?> wrapper){
        return new ColumnRef(tableAlias, rawColumn, columnAlias, wrapper);
    }
    public static ColumnRef of(String tableAlias, String rawColumn, String columnAlias) {
        return of(tableAlias, rawColumn, columnAlias, null);
    }

    /* ========== 工厂：Lambda 字段（自动别名） ========== */
    public static <T, A> ColumnRef of(String tableAlias, SFunction<T, ?> lambda, SFunction<A, ?> columnAlias) {
        String column = LambdaUtils.getColumn(lambda);
        String alias = null;
        if(columnAlias != null) {
            alias = LambdaUtils.getFieldName(lambda);
        }
        ColumnRef ref = of(tableAlias, column, alias);
        if(StrUtil.isBlank(tableAlias)){
            ref.setTableEntityClass(LambdaUtils.getCapturingClass(lambda));
        }
        return ref;
    }

    public static <T, A> ColumnRef of(SFunction<T, ?> lambda, SFunction<A, ?> columnAlias, AbstractWrapper<?, ?> wrapper){
        Class<?> entityClass = LambdaUtils.getCapturingClass(lambda);
        String column = LambdaUtils.getColumn(lambda);
        // 默认用主表（-1）
        String tableAlias = null;
        if(wrapper != null && wrapper.getContext().getAliasRegistry().isRegister(entityClass)) {
            tableAlias = wrapper.resolveAlias(entityClass, -1);
        }
        String alias = null;
        if(columnAlias != null) {
            alias = LambdaUtils.getFieldName(columnAlias);
        }else{
            alias = LambdaUtils.getFieldName(lambda);
        }
        ColumnRef ref = of(tableAlias, column, alias);
        ref.setCurrentWrapper(wrapper);
        if(StrUtil.isBlank(tableAlias)) {
            ref.setTableEntityClass(entityClass);
        }
        return ref;
    }

    public static <T, A> ColumnRef of(SFunction<T, ?> lambda, AbstractWrapper<?, ?> wrapper){
        return of(lambda, wrapper, -1);
    }

    /**
     * 指定 表别名
     * @param lambda 字段
     * @param tableAlias 表别名
     * @return ColumnRef
     * @param <T> 实体类
     */
    public static <T> ColumnRef of(SFunction<T, ?> lambda, String tableAlias) {
        Class<?> entityClass = LambdaUtils.getCapturingClass(lambda);
        String column = LambdaUtils.getColumn(lambda);
        String columnAlias = LambdaUtils.getFieldName(lambda);
        ColumnRef ref = of(tableAlias, column, columnAlias);
        if(StrUtil.isBlank(tableAlias)) {
            ref.setTableEntityClass(entityClass);
        }
        return ref;
    }

    /**
     * 自动识别表别名
     * @param lambda 字段
     * @return ColumnRef
     * @param <T> 实体类
     */
    public static <T> ColumnRef of(SFunction<T, ?> lambda) {
        return of(lambda, "");
    }


    public SqlSegment toSqlSegment(){
        return this;
    }

    /* ========== 工厂：Lambda 字段 + 指定 joinIndex ========== */
    public static <T> ColumnRef of(SFunction<T, ?> lambda, AbstractWrapper<?, ?> wrapper, int joinIndex) {
        Class<?> entityClass = LambdaUtils.getCapturingClass(lambda);
        String column = LambdaUtils.getColumn(lambda);
        String columnAlias = LambdaUtils.getFieldName(lambda);
        String tableAlias = null;
        if(wrapper != null && wrapper.getContext().getAliasRegistry().isRegister(entityClass)) {
            tableAlias = wrapper.resolveAlias(entityClass, -1);
        }
        ColumnRef ref = of(tableAlias, column, columnAlias).withWrapper(wrapper);
        ref.setTableEntityClass(entityClass);
        return ref;
    }

    /**
     * 在输出时，不带字段别名
     * @return
     */
    public ColumnRef withoutAlias() {
        this.useColumnAlias = false;
        return this;
    }

    public ColumnRef withWrapper(AbstractWrapper<?, ?> wrapper){
        this.currentWrapper = wrapper;
        return this;
    }

    public ColumnRef withTableAlias(String tableAlias) {
        this.tableAlias = tableAlias;
        return this;
    }
    public ColumnRef as(String alias) {
        if(StrUtil.isNotBlank(alias)) {
            if (alias.contains(".") && !alias.contains("\"")) {
                alias = "\"" + alias.trim() + "\"";
            } else {
                alias = alias.trim();
            }
            this.columnAlias = alias;
        }
        return this;
    }

    /* ========== SqlSegment ========== */
    @Override
    public String getSql() {
//        if(StrUtil.isBlank(tableAlias)
//                && this.getTableEntityClass() != null
//                && AliasRegistry.getInstance().isRegister(this.getTableEntityClass())){
//            tableAlias = AliasRegistry.getInstance().resolveAlias(this.getTableEntityClass());
//        }
        if(!column.contains(".") && StrUtil.isBlank(this.getTableAlias()) && this.getTableEntityClass() != null){
            tableAlias = calcTableAlias();
        }
        String displayColumn = StrUtil.isBlank(tableAlias) ? column : StrUtil.format("{}.{}", tableAlias, column);
        Column col = new Column(displayColumn);
        Alias alias = null;
        if(this.useColumnAlias){
            alias = new Alias(columnAlias);
        }
        SelectItem<Column> selectItem = new SelectItem<>(col, alias);
//        return StrUtil.format("{}.{} as {}", tableAlias, column, columnAlias);
        return selectItem.toString();
    }
}